package org.sean.framework.es;

import org.elasticsearch.action.DocWriteRequest;
import org.elasticsearch.search.SearchHit;
import org.sean.framework.bean.PageData;
import org.sean.framework.bean.PageQuery;
import org.sean.framework.logging.Logger;
import org.sean.framework.util.GSONUtil;
import org.sean.framework.util.ObjectUtil;
import org.springframework.beans.factory.annotation.Autowired;

import java.io.IOException;
import java.io.Serializable;
import java.lang.reflect.ParameterizedType;
import java.lang.reflect.Type;
import java.util.ArrayList;
import java.util.Collection;
import java.util.Collections;
import java.util.List;
import java.util.Map;
import java.util.function.Function;

/**
 * 实体索引
 *
 * @param <T> T
 */
public abstract class BaseEntityIndex<T extends Entity> {
    private final Type type;
    protected Logger logger = Logger.newInstance(this.getClass());
    @Autowired
    protected CommonOperation operation;

    protected BaseEntityIndex() {
        Class<?> cls = getClass();
        while (!(cls.getGenericSuperclass() instanceof ParameterizedType)) {
            cls = cls.getSuperclass();
        }
        this.type = ((ParameterizedType) cls.getGenericSuperclass()).getActualTypeArguments()[0];
    }

    /**
     * 创建索引
     *
     * @return 是否成功
     */
    public abstract boolean createIndex();

    /**
     * @return 索引名称
     */
    protected abstract String getIndexName();

    /**
     * 创建索引
     *
     * @param index      index
     * @param properties properties
     * @return 是否成功
     */
    protected boolean createIndex(String index, Map<String, Object> properties) {
        return operation.createIndex(index, properties);
    }

    /**
     * 创建索引
     *
     * @param index      index
     * @param alias      alias
     * @param properties properties
     * @return 是否成功
     * @throws IOException 异常
     */
    protected boolean createIndex(String index, String alias, Map<String, Object> properties) throws IOException {
        return operation.createIndex(index, alias, properties);
    }

    /**
     * 删除索引
     *
     * @param index index
     * @return 是否成功
     */
    protected boolean deleteIndex(String index) {
        return operation.deleteIndex(index);
    }


    /**
     * 新增或修改实体数据
     *
     * @param entities 实习信息
     * @param upsert   是否为更新
     * @return 处理结果
     */
    public BulkResult indexOrUpdate(Collection<T> entities, Boolean upsert) {
        if (ObjectUtil.isEmpty(entities)) {
            return new BulkResult();
        }
        List<DocWriteRequest<?>> requests = new ArrayList<>();
        entities.forEach(item -> {
            if (upsert == null) {
                requests.add(operation.indexRequest(getIndexName(), item.getId(), item));
            } else if (upsert) {
                requests.add(operation.updateRequest(getIndexName(), item.getId(), item).docAsUpsert(true));
            } else {
                requests.add(operation.updateRequest(getIndexName(), item.getId(), item));
            }
        });
        return operation.bulk(requests);
    }

    /**
     * 新增或修改实体数据(完整数据)
     *
     * @param entities 实体信息
     * @return 处理结果
     */

    public BulkResult indexOrUpdate(Collection<T> entities) {
        return indexOrUpdate(entities, true);
    }

    /**
     * 新增实体数据
     *
     * @param entities 实体数据
     * @return 处理结果
     */
    public BulkResult index(List<T> entities) {
        return indexOrUpdate(entities, null);
    }

    /**
     * 新增实体数据
     *
     * @param entity 实体数据
     * @return 是否成功
     */
    public boolean index(T entity) {
        if (entity == null || entity.getId() == null) {
            return false;
        }
        return operation.index(getIndexName(), entity.getId().toString(), entity);
    }

    /**
     * 修改实体数据
     *
     * @param entity 实体数据
     * @return 是否成功
     */
    public boolean update(T entity) {
        if (entity == null || entity.getId() == null) {
            return false;
        }
        return operation.update(getIndexName(), entity.getId().toString(), entity);
    }


    /**
     * 修改实体数据
     *
     * @param entities 实体数据
     * @return 响应结果
     */
    public BulkResult update(List<T> entities) {
        return indexOrUpdate(entities, false);
    }

    /**
     * 删除数据
     *
     * @param id id
     * @return 是否删除成功
     */
    public boolean delete(Integer id) {
        if (id == null) {
            return false;
        }
        return operation.delete(getIndexName(), id.toString());
    }

    /**
     * 查询实体
     *
     * @param id id
     * @return 数据
     */
    public T query(Integer id) {
        if (id == null) {
            return null;
        }
        try {
            return GSONUtil.map2Obj(operation.get(getIndexName(), id).getSourceAsMap(), type);
        } catch (Exception e) {
            logger.printStackTrace(e);
            return null;
        }
    }

    /**
     * 查询实体ID
     *
     * @param example 条件
     * @return 数据列表
     */
    public List<Integer> searchIds(QueryExample example) {
        return operation.search4IntegerId(getIndexName(), example, false);
    }


    /**
     * 查询实体信息
     *
     * @param example 条件
     * @return 数据列表
     */
    public List<T> search(QueryExample example) {
        return search(example, false);
    }


    /**
     * 查询全部实体
     *
     * @param example   条件
     * @param needScore 是否需要算分
     * @return 数据列表
     */
    @SuppressWarnings("unchecked")
    public List<T> search(QueryExample example, boolean needScore) {
        return (List<T>) operation.search(getIndexName(), example, needScore, true, null, null, null, type)
                .getList();
    }


    /**
     * 查询实体信息
     *
     * @param example   查询参数
     * @param sortParam 排序参数
     * @return 数据列表
     */
    @SuppressWarnings("unchecked")
    public List<T> searchList(QueryExample example, List<EntitySortParam> sortParam) {
        return (List<T>) operation.search(getIndexName(), example, false, true, null, null, sortParam, type).getList();
    }


    /**
     * 查询实体信息
     *
     * @param example   查询参数
     * @param sortParam 排序参数
     * @return 数据列表
     */
    @SuppressWarnings("unchecked")
    public List<T> searchList(QueryExample example, EntitySortParam sortParam) {
        List<EntitySortParam> sortParams = new ArrayList<>();
        if (sortParam != null) {
            sortParams.add(sortParam);
        }
        return (List<T>) operation.search(getIndexName(), example, false, true, null, null, sortParams, type).getList();
    }

    /**
     * 查询实体信息
     *
     * @param example 查询参数
     * @param page    页信息
     * @return 分页数据
     */
    public PageData<T> search(QueryExample example, PageQuery page) {
        return search(example, false, page);
    }

    /**
     * 查询实体信息
     *
     * @param example 查询参数
     * @param page    页信息
     * @return 分页数据
     */
    public PageData<T> search(QueryExample example, EntityIncrementParam<? extends Serializable> page) {
        if (page == null) {
            return new PageData<>();
        }
        return search(example, false, page);
    }

    /**
     * 查询实体
     *
     * @param example   条件
     * @param needScore 是否需要算分
     * @param page      分页
     * @return 分页数据
     */
    public PageData<T> search(QueryExample example, boolean needScore, PageQuery page) {
        return operation.search(getIndexName(), example, needScore, false, page, null, null, type);
    }

    /**
     * 查询实体
     *
     * @param example   条件
     * @param needScore 是否需要算分
     * @param page      分页
     * @param sortParam 排序
     * @return 分页数据
     */
    public PageData<T> search(QueryExample example, boolean needScore, PageQuery page,
                              List<EntitySortParam> sortParam) {

        return operation.search(getIndexName(), example, needScore, false, page, null, sortParam, type);
    }

    /**
     * 查询实体
     *
     * @param example   条件
     * @param needScore 是否需要算分
     * @param page      分页
     * @param sortParam 排序
     * @return 分页数据
     */
    public PageData<T> search(QueryExample example, boolean needScore, PageQuery page,
                              EntitySortParam sortParam) {
        List<EntitySortParam> sortParams = new ArrayList<>();
        if (sortParam != null) {
            sortParams.add(sortParam);
        }
        return operation.search(getIndexName(), example, needScore, false, page, null, sortParams, type);
    }

    /**
     * 查询实体
     *
     * @param example   条件
     * @param needScore 是否需要算分
     * @param page      增量分页
     * @return 分页数据
     */
    public PageData<T> search(QueryExample example, boolean needScore,
                              EntityIncrementParam<? extends Serializable> page) {
        List<EntitySortParam> sortParams = Collections.singletonList(EntitySortParam.builder().field(page.getField()).sort(page.getSort()).build());
        return operation.search(getIndexName(), example, needScore, false, null, page, sortParams, type);
    }

    /**
     * 查询实体
     *
     * @param example   条件
     * @param needScore 是否需要算分
     * @param page      增量分页
     * @param sortParam 排序
     * @return 分页数据
     */
    public PageData<T> search(QueryExample example, boolean needScore,
                              EntityIncrementParam<? extends Serializable> page, List<EntitySortParam> sortParam) {
        return operation.search(getIndexName(), example, needScore, false, null, page, sortParam, type);
    }

    /**
     * 查询实体
     *
     * @param example   条件
     * @param needScore 是否需要算分
     * @param page      增量分页
     * @param sortParam 排序
     * @return 分页数据
     */
    public PageData<T> search(QueryExample example, boolean needScore,
                              EntityIncrementParam<? extends Serializable> page, EntitySortParam sortParam) {

        List<EntitySortParam> sortParams = new ArrayList<>();
        if (sortParam != null) {
            sortParams.add(sortParam);
        }
        return operation.search(getIndexName(), example, needScore, false, null, page, sortParams, type);
    }

    /**
     * 查询实体
     *
     * @param <E>       E
     * @param example   条件
     * @param needScore 是否需要算分
     * @param fetchAll  是否获取所有数据集(参数为true时,fun处理时间必须小于5秒)
     * @param fun       数据处理方法
     * @param page      增量分页
     * @param sortParam 排序
     * @return 分页数据
     */
    public <E> PageData<E> search(QueryExample example, boolean needScore, boolean fetchAll, Function<SearchHit, E> fun,
                                  EntityIncrementParam<? extends Serializable> page,
                                  EntitySortParam sortParam) {
        List<EntitySortParam> sortParams = new ArrayList<>();
        if (sortParam != null) {
            sortParams.add(sortParam);
        }
        return operation.search(getIndexName(), example, needScore, fun, fetchAll, null, page, sortParams);
    }

}
